/************************************************
 * MirrorOS Kernel Drivers
 * RamFS Driver
 * Copyright 2008 Matthew Iselin
 ***********************************************/

// Defines

// st_mode bitmasks
#define 	_IFMT	0170000	/* type of file */
#define		_IFDIR	0040000	/* directory */
#define		_IFCHR	0020000	/* character special */
#define		_IFBLK	0060000	/* block special */
#define		_IFREG	0100000	/* regular */
#define		_IFLNK	0120000	/* symbolic link */
#define		_IFSOCK	0140000	/* socket */
#define		_IFIFO	0010000	/* fifo */

// Types

// struct stat stuff
#ifdef __CYGWIN__
#include <cygwin/stat.h>
#ifdef _COMPILING_NEWLIB
#define stat64 __stat64
#endif
#else
struct	stat
{
  short		st_dev;
  unsigned short		st_ino;
  unsigned int	st_mode;
  unsigned short	st_nlink;
  unsigned short		st_uid;
  unsigned short		st_gid;
  short		st_rdev;
  long		st_size;
  /* SysV/sco doesn't have the rest... But Solaris, eabi does.  */
#if defined(__svr4__) && !defined(__PPC__) && !defined(__sun__)
  unsigned long	st_atime;
  unsigned long	st_mtime;
  unsigned long	st_ctime;
#else
  unsigned long	st_atime;
  long		st_spare1;
  unsigned long	st_mtime;
  long		st_spare2;
  unsigned long	st_ctime;
  long		st_spare3;
  long		st_blksize;
  long		st_blocks;
  long	st_spare4[2];
#endif
};
#endif

// Prototypes

int dprintf( const char* fmt, ... );

// installs a device
void InstallDevice(	char* prefix,
					unsigned int s_open,
					unsigned int s_read,
					unsigned int s_write,
					unsigned int s_seek,
					unsigned int s_ioctl,
					unsigned int s_stat,
					unsigned int s_close,
					int blocksize
					);

// clears the screen
void kClearScreen( unsigned int x, unsigned int y );

// writes a character
void kWriteChar( unsigned char c, unsigned char a );

// writes a string
void kWriteString( char* s, unsigned char a );

///////////// KEYMAPS /////////////

// US keyboard layout (scancode-based)
unsigned char kbdus[128] =
{
    0,  27, '1', '2', '3', '4', '5', '6', '7', '8',	/* 9 */
  '9', '0', '-', '=', '\b',	/* Backspace */
  '\t',			/* Tab */
  'q', 'w', 'e', 'r',	/* 19 */
  't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n',	/* Enter key */
    0,			/* 29   - Control */
  'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';',	/* 39 */
 '\'', '`',   0,		/* Left shift */
 '\\', 'z', 'x', 'c', 'v', 'b', 'n',			/* 49 */
  'm', ',', '.', '/',   0,				/* Right shift */
  '*',
    0,	/* Alt */
  ' ',	/* Space bar */
    0,	/* Caps lock */
    0,	/* 59 - F1 key ... > */
    0,   0,   0,   0,   0,   0,   0,   0,
    0,	/* < ... F10 */
    0,	/* 69 - Num lock*/
    0,	/* Scroll Lock */
    0,	/* Home key */
    0,	/* Up Arrow */
    0,	/* Page Up */
  '-',
    0,	/* Left Arrow */
    0,
    0,	/* Right Arrow */
  '+',
    0,	/* 79 - End key*/
    0,	/* Down Arrow */
    0,	/* Page Down */
    0,	/* Insert Key */
    0,	/* Delete Key */
    0,   0,   0,
    0,	/* F11 Key */
    0,	/* F12 Key */
    0,	/* All other keys are undefined */
};

// US keyboard layout, with SHIFT pressed
unsigned char kbdus_shift[128] =
{
    0,  27, '!', '@', '#', '$', '%', '^', '&', '*',	/* 9 */
  '(', ')', '_', '+', '\b',	/* Backspace */
  '\t',			/* Tab */
  'Q', 'W', 'E', 'R',	/* 19 */
  'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n',	/* Enter key */
    0,			/* 29   - Control */
  'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':',	/* 39 */
 '"', '~',   0,		/* Left shift */
 '|', 'Z', 'X', 'C', 'V', 'B', 'N',			/* 49 */
  'M', '<', '>', '?',   0,				/* Right shift */
  '*',
    0,	/* Alt */
  ' ',	/* Space bar */
    0,	/* Caps lock */
    0,	/* 59 - F1 key ... > */
    0,   0,   0,   0,   0,   0,   0,   0,
    0,	/* < ... F10 */
    0,	/* 69 - Num lock*/
    0,	/* Scroll Lock */
    0,	/* Home key */
    0,	/* Up Arrow */
    0,	/* Page Up */
  '-',
    0,	/* Left Arrow */
    0,
    0,	/* Right Arrow */
  '+',
    0,	/* 79 - End key*/
    0,	/* Down Arrow */
    0,	/* Page Down */
    0,	/* Insert Key */
    0,	/* Delete Key */
    0,   0,   0,
    0,	/* F11 Key */
    0,	/* F12 Key */
    0,	/* All other keys are undefined */
};

///////////// END KEYMAPS /////////////

// Variables

// whether shift is pressed
char shift_pressed = 0;

// the character input buffer
char charin[1024];

// an open tty stream
struct desc {
	char attrib; // this stream's character attribute
	int avail; // whether this entry is available
};
volatile struct desc desctable[4096]; // tty devices are popular - don't exhaust their needs

// Functions

// opens a file
int tty_open( const char* path, int flags )
{
	// find an available descriptor
	int i;
	for( i = 0; i < 4096; i++ )
		if( desctable[i].avail )
			break;
	
	// one found?
	if( i == 4096 )
		return -1; // died
	
	// no longer available, and set a default attribute
	desctable[i].avail = 0;
	desctable[i].attrib = 0x0F;
	
	// return the offset
	return i;
}

// reads from a file
unsigned long tty_read( int fd, void* buff, unsigned long count )
{	
	// check for a valid descriptor
	if( fd < 0 || fd >= 4096 || desctable[fd].avail )
		return 0;
	
	// this holds an offset into the charin buffer, it's basically the last free character
	int z = 0;

	// read enough bytes
	char* b = (char*) buff;
	unsigned long i;
	for( i = 0; i < count; i++ )
	{
		// if we've hit the end of the buffer, jump to the start again
		if( charin[z] == 0 )
			z = 0;
		
		// wait until a character is ready
		while( charin[z] == 0 );
		
		// and then write it to the buffer
		*b++ = charin[z];
		
		// store the character read
		char c = charin[z];
		
		// remove the character from the buffer
		charin[z] = 0;
		
		// increment the buffer index
		z++;
	}
	
	// add a null character to the end of the string
	*b++ = 0;
	
	// return the number of bytes read in
	return i;
}

// writes to a file
unsigned long tty_write( int fd, void* buff, unsigned long count )
{
	// check for a valid descriptor
	if( fd < 0 || fd >= 4096 || desctable[fd].avail )
		return 0;
	
	// grab a usable pointer to the buffer
	char* b = (char*) buff;
	
	// write out each character
	unsigned long i;
	for( i = 0; i < count; i++ )
		kWriteChar( b[i], desctable[fd].attrib );
	return i;
}

// seeks within the file
unsigned long tty_seek( int fd, unsigned long off, int set )
{
	// always at 0
	return 0;
}

// controls the driver itself (in this driver anyway)
int tty_ioctl( int fd, int action, int data )
{
	// check for a valid descriptor
	if( fd < 0 || fd >= 4096 || desctable[fd].avail )
		return 0;
	
	// what is the action?
	if( action == 1 )
		desctable[fd].attrib = (char) data;
	
	// successful
	return 0;
}

// gets information about a specific open descriptor
int tty_stat( int fd, struct stat* buf )
{
	// all TTY devices have the same size and characteristics
	buf->st_mode = _IFCHR;
	buf->st_size = 1;
	buf->st_blocks = buf->st_size;

	// success, no failure
	return 0;
}

// closes a descriptor
int tty_close( int fd )
{
	// check for a valid descriptor
	if( fd < 0 || fd >= 4096 || desctable[fd].avail )
		return 0;
	
	// make it available again
	desctable[fd].avail = 1;
	
	// success!
	return 0;
}

// handles the keyboard IRQ
void tty_keyisr( void* p )
{	
	// the scancode of the key just hit
	unsigned char scancode;

	// read it in from the data buffer
	scancode = inb( 0x60 );

	// was the key just released?
	if( scancode & 0x80 )
	{
		// check for shifting
		switch( scancode )
		{
			// left/right shift keys
			case 54:
			case 182:
				
				// not shifting anymore
				shift_pressed = 0;
				
				break;
		}
	}
	else
	{
		// shifting?
		switch( scancode )
		{
			// left/right shift keys
			case 54:
			case 182:
				
				// we are shifting
				shift_pressed = 1;
			
				// don't save the characters
				return;
				
				break;
		}
		
		// add to the character buffer
		int cbuff_off;
		for( cbuff_off = 0; cbuff_off < 1024; cbuff_off++ )
			if( charin[cbuff_off] == 0 )
				break;
		
		// is there no more space?
		if( cbuff_off == 1024 )
			return; // trash the keypress
		
		// put it into the buffer
		if( shift_pressed )
		{
			if( kbdus_shift[scancode] == 0 )
				charin[cbuff_off] = scancode | 0x80; // special chars should send their scancode
			else
				charin[cbuff_off] = kbdus_shift[scancode];
		}
		else
		{
			if( kbdus[scancode] == 0 )
				charin[cbuff_off] = scancode | 0x80; // special chars should send their scancode
			else
				charin[cbuff_off] = kbdus[scancode];
		}
	}
}

// installs the driver and sets it up properly
void dmain( int argc, char* argv[] )
{	
	// set all the descriptors as available
	int i;
	for( i = 0; i < 4096; i++ )
		desctable[i].avail = 1;
	
	// zero the character buffer
	for( i = 0; i < 1024; i++ )
		charin[i] = 0;
	
	// install the device calls
	InstallDevice( "/dev/tty", (unsigned int) tty_open, (unsigned int) tty_read, (unsigned int) tty_write, (unsigned int) tty_seek, (unsigned int) tty_ioctl, (unsigned int) tty_stat, (unsigned int) tty_close, 1 );
	
	// install our keyboard IRQ handler
	kInstallCustomIntrHandler( 33, tty_keyisr );
}
